﻿using mozhi.smarterp.Finance.Accounts.Repository;
using mozhi.smarterp.Finance.Banks;
using mozhi.smarterp.Finance.Banks.Repository;
using mozhi.smarterp.Finance.Finances.Repository;
using mozhi.smarterp.Finance.Finances.Services;
using mozhi.smarterp.Finance.Querys;
using Mozhi.Abp.Core.Extensions;
using Mozhi.Abp.Domain.BasicData;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Services;
using Volo.Abp.Guids;

namespace mozhi.smarterp.Finance.Accounts.Services
{
    public class AccountBalanceManager : DomainService
    {
        private IAccountBalanceRepository _repository;
        private IAccountRepository _accountRepository;
        private ISettlementRepository _settlementRepository;
        private IGuidGenerator _guidGenerator;
        private IBankAccountRepository _bankAccountRepository;
        private IJournalRepository _journalRepository;
        private JournalManager _journalManager;
        private IOrganizationRepository _organizationRepository;

        public AccountBalanceManager(IAccountBalanceRepository repository, IAccountRepository accountRepository, IBankAccountRepository bankAccountRepository,
            ISettlementRepository settlementRepository, IGuidGenerator guidGenerator , IOrganizationRepository organizationRepository,
            IJournalRepository journalRepository, JournalManager journalManager)
        {
            _repository = repository;
            _accountRepository = accountRepository;
            _settlementRepository = settlementRepository;
            _guidGenerator = guidGenerator;
            _bankAccountRepository = bankAccountRepository;
            _journalRepository = journalRepository;
            _journalManager = journalManager;
            _organizationRepository = organizationRepository;
        }

        /// <summary>
        /// 计算科目余额
        /// </summary>
        /// <param name="balance"></param>
        /// <param name="balances"></param>
        /// <param name="accounts"></param>
        /// <param name="bankAccounts"></param>
        /// <param name="baseCurrencyId"></param>
        /// <returns></returns>
        /// <exception cref="BusinessException"></exception>
        public async Task CalculateAsync(AccountBalance balance, List<AccountBalance> balances, List<Account> accounts, List<BankAccount> bankAccounts, Guid baseCurrencyId)
        {
            //var account = accounts.FirstOrDefault(x => x.Id == balance.AccountId);

            //var bankAccount = bankAccounts.FirstOrDefault(x => x.Id == account.BankAccountId);

            //var matchBalance = balances.FirstOrDefault(x => x.AccountId == balance.AccountId && x.CurrencyId==balance.CurrencyId && x.DimensionGroupValueId == balance.DimensionGroupValueId && x.Period == balance.Period);

            //if (matchBalance == null)
            //    balances.Add(balance);
            //else
            //{
            //    matchBalance.ChangeAmount(account.IsForeignCurrency, baseCurrencyId, bankAccount?.CurrencyId,
            //        balance.OpeningAmount, balance.OpeningAmountFr,
            //        balance.CurrentYearDebitAmount, balance.CurrentYearDebitAmountFr, balance.CurrentYearCreditAmount, balance.CurrentYearCreditAmountFr,
            //        balance.DebitAmount, balance.DebitAmountFr, balance.CreditAmount, balance.CreditAmountFr);
            //}

            ////先计算和余额合计行
            //var currencyTotalBalances = balances.Where(x => x.AccountId == balance.AccountId && x.DimensionGroupValueId == null && x.CurrencyId != null).ToList();

            //var groupBalancesByCurrency = balances.Where(x => x.AccountId == balance.AccountId && x.DimensionGroupValueId.HasValue && x.CurrencyId.HasValue).GroupBy(x => x.CurrencyId);

            //foreach (IGrouping<Guid?, AccountBalance> info in groupBalancesByCurrency)
            //{
            //    var matchCurrencyTotalBalance = currencyTotalBalances.FirstOrDefault(x => x.CurrencyId == info.Key);

            //    if (matchCurrencyTotalBalance == null)
            //    {
            //        matchCurrencyTotalBalance = new AccountBalance(_guidGenerator.Create(), account.Id, info.Key, balance.Period, null, account.Direction);
            //        balances.Add(matchCurrencyTotalBalance);
            //    }

            //    SetTotalBalance(matchCurrencyTotalBalance, info.ToList(), baseCurrencyId);
            //}

            ////计算不含币别和核算维度合计行

            //var totalBalance = balances.FirstOrDefault(x => x.AccountId == balance.AccountId && !x.CurrencyId.HasValue && !x.DimensionGroupValueId.HasValue);

            //if (totalBalance == null)
            //{
            //    totalBalance = new AccountBalance(_guidGenerator.Create(), balance.AccountId, null, balance.Period, null, balance.Direction);
            //    balances.Add(totalBalance);
            //}

            //currencyTotalBalances = balances.Where(x => x.AccountId == balance.AccountId && x.DimensionGroupValueId == null && x.CurrencyId != null).ToList();

            //SetTotalBalance(totalBalance, currencyTotalBalances, baseCurrencyId);

            ////没有父级科目，就不处理父级了
            //if (!account.ParentId.HasValue)
            //    return;

            ////开始处理父级科目的余额
            //var parentAccount = accounts.FirstOrDefault(x => x.Id == account.ParentId);

            //if (parentAccount == null)
            //    throw new BusinessException("100000", $"父级科目：{account.ParentId}不存在");

            //await CalculateParentAsync(parentAccount, balances, accounts, baseCurrencyId);
            throw new NotImplementedException();

        }

        /// <summary>
        /// 计算父级科目余额
        /// </summary>
        /// <param name="Parent"></param>
        /// <param name="balances"></param>
        /// <param name="children"></param>
        /// <param name="baseCurrencyId"></param>
        /// <returns></returns>
        public async Task CalculateParentAsync(Account parent, List<AccountBalance> balances, List<Account> accounts, Guid baseCurrencyId)
        {
            var childAccounts = await _accountRepository.GetListAsync(x=>x.ParentId == parent.Id);

            if (childAccounts.Count == 0)
                return;

            var childAccountIds = childAccounts.Select(x => x.Id).ToList();

            var childAccountBalances = balances.Where(x => x.CurrencyId.HasValue && !x.DimensionGroupValueId.HasValue && childAccountIds.Contains(x.AccountId)).ToList();

            var groupBalancesByCurrency = childAccountBalances.GroupBy(x => x.CurrencyId);

            foreach (IGrouping<Guid?, AccountBalance> info in groupBalancesByCurrency)
            {
                var matchCurrencyTotalBalance = balances.FirstOrDefault(x => x.AccountId == parent.Id && !x.DimensionGroupValueId.HasValue && x.CurrencyId == info.Key);

                if (matchCurrencyTotalBalance == null)
                {
                    matchCurrencyTotalBalance = new AccountBalance(_guidGenerator.Create(), parent.Id, info.Key, balances.First().Period, null, parent.Direction);
                    balances.Add(matchCurrencyTotalBalance);
                }

                SetTotalBalance(matchCurrencyTotalBalance, info.ToList(), baseCurrencyId);
            }

            //计算不含币别和核算维度合计行

            var totalBalance = balances.FirstOrDefault(x => x.AccountId == parent.Id && !x.CurrencyId.HasValue && !x.DimensionGroupValueId.HasValue);

            if (totalBalance == null)
            {
                totalBalance = new AccountBalance(_guidGenerator.Create(), parent.Id, null, balances.First().Period, null, parent.Direction);
                balances.Add(totalBalance);
            }

            SetTotalBalance(totalBalance, childAccountBalances, baseCurrencyId);

            if (!parent.ParentId.HasValue)
                return;

            //在递归处理父级的父级
            var parentAccount = accounts.FirstOrDefault(x => x.Id == parent.ParentId);

            if (parentAccount == null)
                throw new BusinessException("100000", $"父级科目：{parent.ParentId}不存在");

            await CalculateParentAsync(parentAccount, balances, accounts, baseCurrencyId);
        }

        public async Task UpdateAsync(Guid accountId, List<AccountBalance> processBalances , List<AccountBalance> balances , List<Account> accounts , List<BankAccount> bankAccounts)
        {
            //var account = await _accountRepository.FindAsync(accountId);

            ////1.科目是存在
            //ValidateAccount(account);

            ////2.查找组织是否已经启用
            //var organization = await _organizationRepository.GetAsync();

            //organization.ValidateFinance();

            //var bankAccount = account.BankAccountId.HasValue ? await _bankAccountRepository.FindAsync(account.BankAccountId.Value) : null;

            //var periods = processBalances.Select(x => x.Period).Distinct().ToList();

            //await ValidateSettlement(periods);

            //processBalances = MergeBalance(organization , account, processBalances);



            //foreach (var balance in processBalances)
            //{
            //    await CalculateAsync(balance, balances, accounts, bankAccounts, organization.StandardCurrencyId.Value);
            //}
            throw new NotImplementedException();
        }


        public async Task DeleteAsync(AccountBalance balance , List<AccountBalance> balances, List<Account> accounts, List<BankAccount> bankAccounts, Guid baseCurrencyId)
        {
            if (balance == null)
                throw new UserFriendlyException("科目余额不存在", "100000");

            await ValidateSettlement(new List<int>() { balance.Period });

            var matchBalance = balances.FirstOrDefault(x => x.Id == balance.Id);

            if(matchBalance==null)
                throw new UserFriendlyException("科目余额不存在", "100000");

            var account = accounts.FirstOrDefault(x => x.Id == balance.AccountId);

            matchBalance.ChangeAmount(account.IsForeignCurrencyAccounting, baseCurrencyId, null, null, null, null, null, null, null, null, null, null, null);

            await CalculateAsync(balance , balances , accounts , bankAccounts , baseCurrencyId);

        }

        

        public async Task ValidateAccountAsync(Guid id)
        {
            var account = await _accountRepository.FindAsync(id);

            ValidateAccount(account);
        }

        public void ValidateAccount(Account account)
        {
            //if (account == null)
            //    throw new UserFriendlyException("科目不存在", "100000");

            //if (!account.Activated)
            //    throw new UserFriendlyException("科目已经禁用", "100000");

            //if (!account.IsLeaf)
            //    throw new UserFriendlyException("非子级科目不允许新增和修改科目余额", "100000");

            //if (account.IsFundAccount && !account.BankAccountId.HasValue)
            //    throw new UserFriendlyException("资金科目必须绑定银行账号", "100000");
            throw new NotImplementedException();
        }

        /// <summary>
        /// 获取未结账期间的科目余额
        /// </summary>
        /// <param name="startPeriod"></param>
        /// <param name="endPeriod"></param>
        /// <returns></returns>
        public async Task<List<AccountBalance>> GetUnClosePeriodBalances(int period, bool includeDimenssion = true, bool includeCurrency = true, bool includeUnapproveJournal = false)
        {
            var result = new List<AccountBalance>();

            //查找组织是否已经启用
            var organization = await _organizationRepository.GetAsync();

            organization.ValidateFinance();

            var openingPeriod = organization.ActivatedDate?.ToPeriod();

            if (openingPeriod == null)
                throw new UserFriendlyException("组织未设置启用日期", "100000");

            if (period < openingPeriod)
                throw new UserFriendlyException("当前期间不能小于启用期间", "100000");

            var initailzePeriods = await _settlementRepository.FindAsync(new SettlementQuery() { Period = openingPeriod.Value.ToPrePeriod() });

            //未完成初始化
            if (initailzePeriods == null || initailzePeriods.Count == 0)
                throw new UserFriendlyException("组织未完成初始化", "100000");

            var lastClosePeriod = await _settlementRepository.GetLastClosePeriod();

            if (lastClosePeriod >= period)
                throw new UserFriendlyException($"期间{period}已结账", "100000");

            var lastCloseBalances = await _repository.FindAsync(new AccountBalanceQuery() { Period = lastClosePeriod, IncludeDimension = includeDimenssion, IncludeCurrency = includeCurrency });

            var differencePeriod = lastClosePeriod.DifferencePeriod(period);

            int currentPeriod = lastClosePeriod.ToNextPeriod();

            var currentPeriodBalances = lastCloseBalances;

            for (int i = 0; i < differencePeriod; i++)
            {
                //1.将上一期的转换为当期的余额
                currentPeriodBalances = await CalculateCurrentPeriodBalances(currentPeriodBalances, currentPeriod, includeUnapproveJournal , organization);

                //加入到结果中
                result.AddRange(currentPeriodBalances);

                currentPeriod = currentPeriod.ToNextPeriod();
            }

            return result;
        }

        public async Task ValidateSettlement(List<int> periods)
        {
            var settlements = await _settlementRepository.FindAsync(new SettlementQuery() { Periods = periods, Closed = true });

            if (settlements.Count > 0)
            {
                var settlementPeriods = settlements.Select(x => x.Period).Distinct().ToList();

                throw new UserFriendlyException($"已完成初始化或者期间已结账：{string.Join(",", settlementPeriods)}", "100000");
            }
        }

        #region
        private List<AccountBalance> MergeBalance(Organization organization , Account account, List<AccountBalance> balances)
        {
            var groups = balances.GroupBy(x => new { x.AccountId, x.CurrencyId, x.Period, x.DimensionGroupValueId });

            var result = new List<AccountBalance>();

            foreach (IGrouping<object, AccountBalance> info in groups)
            {
                var standard = info.First();

                var openingAmount = info.Where(x => x.OpeningAmount.HasValue).Sum(x => x.OpeningAmount);
                var openingAmountFr = info.Where(x => x.OpeningAmountFr.HasValue).Sum(x => x.OpeningAmountFr);

                var currentYearDebitAmount = info.Where(x => x.CurrentYearDebitAmount.HasValue).Sum(x => x.CurrentYearDebitAmount);
                var currentYearDebitAmountFr = info.Where(x => x.CurrentYearDebitAmountFr.HasValue).Sum(x => x.CurrentYearDebitAmountFr);

                var currentYearCreditAmount = info.Where(x => x.CurrentYearCreditAmount.HasValue).Sum(x => x.CurrentYearCreditAmount);
                var currentYearCreditAmountFr = info.Where(x => x.CurrentYearCreditAmountFr.HasValue).Sum(x => x.CurrentYearCreditAmountFr);

                var debitAmount = info.Where(x => x.DebitAmount.HasValue).Sum(x => x.DebitAmount);
                var debitAmountFr = info.Where(x => x.DebitAmountFr.HasValue).Sum(x => x.DebitAmountFr);

                var creditAmount = info.Where(x => x.CreditAmount.HasValue).Sum(x => x.CreditAmount);
                var creditAmountFr = info.Where(x => x.CreditAmountFr.HasValue).Sum(x => x.CreditAmountFr);

                standard.ChangeAmount(account.IsForeignCurrencyAccounting, organization.StandardCurrencyId.Value, null, openingAmount, openingAmountFr, currentYearDebitAmount, currentYearDebitAmountFr
                     , currentYearCreditAmount, currentYearCreditAmountFr,
                    debitAmount, debitAmountFr, creditAmount, creditAmountFr);

                result.Add(standard);
            }

            return result;
        }


        public async Task<List<AccountBalance>> CalculateCurrentPeriodBalances(List<AccountBalance> prePeriodBalances, int period, bool includeUnapproveJournal , Organization organization)
        {
            //var result = new List<AccountBalance>();

            ////1. 将上一期的科目余额转换为本期的科目余额
            //var accounts = await _accountRepository.GetListAsync();

            //var bankAccountIds = accounts.Where(x => x.BankAccountId.HasValue).Select(x => x.BankAccountId.Value).ToList();

            //var bankAccounts = await _bankAccountRepository.FindAsync(new QueryBase() { Ids = bankAccountIds });

            //var baseCurrencyId = organization.StandardCurrencyId.Value;

            //foreach (var prePeriodBalance in prePeriodBalances)
            //{
            //    var currentPeriodBalance = new AccountBalance(_guidGenerator.Create(), prePeriodBalance.AccountId, prePeriodBalance.CurrencyId, period, prePeriodBalance.DimensionGroupValueId, prePeriodBalance.Direction);

            //    var matchAccount = accounts.FirstOrDefault(x => x.Id == prePeriodBalance.AccountId);

            //    var matchBankAccount = bankAccounts.FirstOrDefault(x => x.Id == matchAccount.BankAccountId);

            //    currentPeriodBalance.ChangeAmount(matchAccount.IsForeignCurrency, baseCurrencyId, matchBankAccount?.CurrencyId,
            //        prePeriodBalance.ClosingAmount, prePeriodBalance.ClosingAmountFr, prePeriodBalance.CurrentYearDebitAmount, prePeriodBalance.CurrentYearDebitAmountFr,
            //        prePeriodBalance.CurrentYearCreditAmount, prePeriodBalance.CurrentYearCreditAmountFr, null, null, null, null);

            //    result.Add(currentPeriodBalance);
            //}

            ////2.将本期的审核的凭证计算到本期发生额
            //var journals = await _journalRepository.FindAsync(new JournalPageQuery() { Approved = includeUnapproveJournal == true ? null : true, Period = period, IncludeDetail = true });

            //if (journals.Count == 0)
            //    return result;

            //var processBalances = new List<AccountBalance>();

            //foreach (var journal in journals)
            //{
            //    var balancesFromJournal = _journalManager.ConvertToBalance(journal);

            //    foreach (var balance in balancesFromJournal)
            //    {
            //        var matchBalance = result.FirstOrDefault(x => x.AccountId == balance.AccountId && x.CurrencyId == balance.CurrencyId && x.DimensionGroupValueId == balance.DimensionGroupValueId);

            //        if (matchBalance == null)
            //            result.Add(balance);
            //        else
            //        {
            //            var matchAccount = accounts.FirstOrDefault(x => x.Id == balance.AccountId);
            //            var matchBankAccount = bankAccounts.FirstOrDefault(x => x.Id == matchAccount.BankAccountId);

            //            decimal? currentYearDebitAmount = matchBalance.CurrentYearDebitAmount == null && balance.DebitAmount == null ? null : (matchBalance.CurrentYearDebitAmount ?? 0) + (balance.DebitAmount ?? 0);
            //            decimal? currentYearDebitAmountFr = matchBalance.CurrentYearDebitAmountFr == null && balance.DebitAmountFr == null ? null : (matchBalance.CurrentYearDebitAmountFr ?? 0) + (balance.DebitAmountFr ?? 0);

            //            decimal? currentYearCreditAmount = matchBalance.CurrentYearCreditAmount == null && balance.CreditAmount == null ? null : (matchBalance.CurrentYearCreditAmount ?? 0) + (balance.CreditAmount ?? 0);
            //            decimal? currentYearCreditAmountFr = matchBalance.CurrentYearCreditAmountFr == null && balance.CreditAmountFr == null ? null : (matchBalance.CurrentYearCreditAmountFr ?? 0) + (balance.CreditAmountFr ?? 0);

            //            decimal? debitAmount = matchBalance.DebitAmount == null && balance.DebitAmount == null ? null : (matchBalance.DebitAmount ?? 0) + (balance.DebitAmount ?? 0);
            //            decimal? debitAmountFr = matchBalance.DebitAmountFr == null && balance.DebitAmountFr == null ? null : (matchBalance.DebitAmountFr ?? 0) + (balance.DebitAmountFr ?? 0);
            //            decimal? creditAmount = matchBalance.CreditAmount == null && balance.CreditAmount == null ? null : (matchBalance.CreditAmount ?? 0) + (balance.CreditAmount ?? 0);
            //            decimal? creditAmountFr = matchBalance.CreditAmountFr == null && balance.CreditAmountFr == null ? null : (matchBalance.CreditAmountFr ?? 0) + (balance.CreditAmountFr ?? 0);

            //            matchBalance.ChangeAmount(matchAccount.IsForeignCurrency, baseCurrencyId, matchBankAccount?.CurrencyId,
            //                matchBalance.OpeningAmount, matchBalance.OpeningAmountFr,
            //                currentYearDebitAmount, currentYearDebitAmountFr, currentYearCreditAmount, currentYearCreditAmountFr,
            //                debitAmount, debitAmountFr, creditAmount, creditAmountFr);
            //        }

            //        processBalances.Add(balance);
            //    }
            //}

            //foreach(var processBalance in processBalances)
            //{
            //    await CalculateAsync(processBalance, result, accounts, bankAccounts, baseCurrencyId);
            //}

            //return result;
            throw new NotImplementedException();
        }


        private void SetTotalBalance(AccountBalance totalBalance, List<AccountBalance> balances, Guid baseCurrencyId)
        {
            var openingAmount = balances.Where(x => x.OpeningAmount.HasValue && x.Direction == totalBalance.Direction).Sum(x => x.OpeningAmount) +
                   balances.Where(x => x.OpeningAmount.HasValue && x.Direction != totalBalance.Direction).Sum(x => x.OpeningAmount * -1);

            var openingAmountFr = balances.Where(x => x.OpeningAmountFr.HasValue && x.Direction == totalBalance.Direction).Sum(x => x.OpeningAmountFr) +
                balances.Where(x => x.OpeningAmountFr.HasValue && x.Direction != totalBalance.Direction).Sum(x => x.OpeningAmountFr * -1);

            var debitAmount = balances.Where(x => x.DebitAmount.HasValue && x.Direction == totalBalance.Direction).Sum(x => x.DebitAmount) +
                balances.Where(x => x.DebitAmount.HasValue && x.Direction != totalBalance.Direction).Sum(x => x.DebitAmount * -1);

            var debitAmountFr = balances.Where(x => x.DebitAmountFr.HasValue && x.Direction == totalBalance.Direction).Sum(x => x.DebitAmountFr) +
                balances.Where(x => x.DebitAmountFr.HasValue && x.Direction != totalBalance.Direction).Sum(x => x.DebitAmountFr * -1);

            var creditAmount = balances.Where(x => x.CreditAmount.HasValue && x.Direction == totalBalance.Direction).Sum(x => x.CreditAmount) +
                balances.Where(x => x.CreditAmount.HasValue && x.Direction != totalBalance.Direction).Sum(x => x.CreditAmount * -1);

            var creditAmountFr = balances.Where(x => x.CreditAmountFr.HasValue && x.Direction == totalBalance.Direction).Sum(x => x.CreditAmountFr) +
                balances.Where(x => x.CreditAmountFr.HasValue && x.Direction != totalBalance.Direction).Sum(x => x.CreditAmountFr * -1);

            var currentYearDebitAmount = balances.Where(x => x.CurrentYearDebitAmount.HasValue && x.Direction == totalBalance.Direction).Sum(x => x.CurrentYearDebitAmount) +
                balances.Where(x => x.CurrentYearDebitAmount.HasValue && x.Direction != totalBalance.Direction).Sum(x => x.CurrentYearDebitAmount * -1);

            var currentYearDebitAmountFr = balances.Where(x => x.CurrentYearDebitAmountFr.HasValue && x.Direction == totalBalance.Direction).Sum(x => x.CurrentYearDebitAmountFr) +
                balances.Where(x => x.CurrentYearDebitAmountFr.HasValue && x.Direction != totalBalance.Direction).Sum(x => x.CurrentYearDebitAmountFr * -1);

            var currentYearCreditAmount = balances.Where(x => x.CurrentYearCreditAmount.HasValue && x.Direction == totalBalance.Direction).Sum(x => x.CurrentYearCreditAmount) +
                balances.Where(x => x.CurrentYearCreditAmount.HasValue && x.Direction != totalBalance.Direction).Sum(x => x.CurrentYearCreditAmount * -1);

            var currentYearCreditAmountFr = balances.Where(x => x.CurrentYearCreditAmountFr.HasValue && x.Direction == totalBalance.Direction).Sum(x => x.CurrentYearCreditAmountFr) +
                balances.Where(x => x.CurrentYearCreditAmountFr.HasValue && x.Direction != totalBalance.Direction).Sum(x => x.CurrentYearCreditAmountFr * -1);

            totalBalance.ChangeAmount(true, baseCurrencyId, null,
                openingAmount, openingAmountFr,
                currentYearDebitAmount, currentYearDebitAmountFr,
                currentYearCreditAmount, currentYearCreditAmountFr,
                debitAmount, debitAmountFr, creditAmount, creditAmountFr);
        }

        #endregion
    }
}
